package cn.iocoder.yudao.framework.web;

import cn.iocoder.yudao.framework.util.SwaggerUtils;
import cn.iocoder.yudao.framework.web.controller.error.CustomErrorController;
import cn.iocoder.yudao.framework.web.controller.error.ErrorInfoBuilder;
import cn.iocoder.yudao.framework.web.controller.hello.HelloController;
import cn.iocoder.yudao.framework.web.servlet.AdminPasswordInterceptor;
import cn.iocoder.yudao.framework.web.servlet.IpUrlLimitInterceptor;
import cn.iocoder.yudao.framework.web.servlet.MdcTraceIdFilter;
import jakarta.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 自动配置
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Slf4j
@AutoConfiguration(before = ErrorMvcAutoConfiguration.class) // 在 ErrorMvcAutoConfiguration 之前配置，以便覆盖默认的 /error
@EnableConfigurationProperties(ServerProperties.class)
public class FallbackWebMvcAutoConfiguration implements WebMvcConfigurer {

    @Bean
    @ConditionalOnMissingBean
    public ErrorInfoBuilder errorInfoBuilder(ServerProperties errorProperties) {
        return new ErrorInfoBuilder(errorProperties);
    }

    @Bean
    @ConditionalOnMissingBean
    public CustomErrorController customErrorController(ErrorInfoBuilder errorInfoBuilder) {
        return new CustomErrorController(errorInfoBuilder);
    }

    @Bean
    @ConditionalOnMissingBean
    public HelloController helloController() {
        return new HelloController();
    }

    @Bean
    @ConditionalOnMissingBean
    public GroupedOpenApi fallbackAllApi() {
        // Apache HTTP  Server 或 NGINX 需要配置反向代理规则，如：ProxyPassMatch ^/(app-api|admin-api|error|endpoint|captcha|hello)(/.*)$ http://127.0.0.1:58080/$1$2
        return SwaggerUtils.buildGroupedApi("fallback-api", "/hello", "/error", "/endpoint", "/captcha");
    }

    @Bean
    @ConditionalOnMissingBean
    public GroupedOpenApi adminAllApi() {
        return SwaggerUtils.buildGroupedAdminApi("admin-api", "**");
    }

    @Bean
    @ConditionalOnMissingBean
    public GroupedOpenApi appAllApi() {
        return SwaggerUtils.buildGroupedAppApi("app-api", "**");
    }

    @Bean
    @ConditionalOnMissingBean
    public GroupedOpenApi openAllApi() {
        return SwaggerUtils.buildGroupedOpenApi("open-api", "**");
    }

    @Bean
    public FilterRegistrationBean<MdcTraceIdFilter> traceIdFilter() {
        FilterRegistrationBean<MdcTraceIdFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new MdcTraceIdFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        return registrationBean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.debug("注册 Server 入口请求拦截器");
        registry.addInterceptor(new IpUrlLimitInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new AdminPasswordInterceptor()).addPathPatterns("/admin-api/**");
    }

    @Override
    public void configurePathMatch(@Nonnull PathMatchConfigurer configurer) {
        AntPathMatcher antPathMatcher = new AntPathMatcher(".");
        configurer.addPathPrefix("/", clazz -> clazz.isAnnotationPresent(RestController.class) && antPathMatcher.match("**.web.controller.**", clazz.getPackage().getName()));
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.debug("注册 Server 入口静态资源处理器");
        // 自定义404、500等出错页面
        registry.addResourceHandler("/error/**")
                .addResourceLocations("classpath:/error/");
        // 映射浏览器图标、品牌商标、占位图等资源路径
        registry.addResourceHandler("/public/**", "/favicon.ico", "/logo.png", "/placeholder.png", "/avatar.png")
                .addResourceLocations("classpath:/public/");
    }

}
